use serde::de;
use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};

use std::cmp::{Eq, PartialEq};
use std::fmt;
use std::hash::Hash;

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum TaskType {
    AddPiece,
    PreCommit1,
    PreCommit2,
    Commit1,
    Commit2,
    Finalize,
    Fetch,
    Unseal,
    ReplicaUpdate,
    ProveReplicaUpdate1,
    ProveReplicaUpdate2,
    RegenSectorKey,
    FinalizeReplicaUpdate,
}

impl TaskType {
    pub fn from_str(value: &str) -> anyhow::Result<Self> {
        match value {
            "seal/v0/addpiece" | "AP" => Ok(TaskType::AddPiece),
            "seal/v0/precommit/1" | "PC1" => Ok(TaskType::PreCommit1),
            "seal/v0/precommit/2" | "PC2" => Ok(TaskType::PreCommit2),
            "seal/v0/commit/1" | "C1" => Ok(TaskType::Commit1),
            "seal/v0/commit/2" | "C2" => Ok(TaskType::Commit2),
            "seal/v0/finalize" | "FIN" => Ok(TaskType::Finalize),
            "seal/v0/fetch" | "GET" => Ok(TaskType::Fetch),
            "seal/v0/unseal" | "UNS" => Ok(TaskType::Unseal),
            "seal/v0/replicaupdate" | "RU" => Ok(TaskType::ReplicaUpdate),
            "seal/v0/provereplicaupdate/1" | "PR1" => Ok(TaskType::ProveReplicaUpdate1),
            "seal/v0/provereplicaupdate/2" | "PR2" => Ok(TaskType::ProveReplicaUpdate2),
            "seal/v0/regensectorkey" | "GSK" => Ok(TaskType::RegenSectorKey),
            "seal/v0/finalize/replicaupdate" | "FRU" => Ok(TaskType::FinalizeReplicaUpdate),
            _ => Err(anyhow::Error::msg(format!(
                "unknown task type string {}.",
                value
            ))),
        }
    }

    #[allow(dead_code)]
    pub fn short(&self) -> &str {
        match self {
            &Self::AddPiece => "AP",
            &Self::PreCommit1 => "PC1",
            &Self::PreCommit2 => "PC2",
            &Self::Commit1 => "C1",
            &Self::Commit2 => "C2",
            &Self::Finalize => "FIN",
            &Self::Fetch => "GET",
            &Self::Unseal => "UNS",
            &Self::ReplicaUpdate => "RU",
            &Self::ProveReplicaUpdate1 => "PR1",
            &Self::ProveReplicaUpdate2 => "PR2",
            &Self::RegenSectorKey => "GSK",
            &Self::FinalizeReplicaUpdate => "FRU",
        }
    }

    pub fn name(&self) -> &str {
        match self {
            &Self::AddPiece => "seal/v0/addpiece",
            &Self::PreCommit1 => "seal/v0/precommit/1",
            &Self::PreCommit2 => "seal/v0/precommit/2",
            &Self::Commit1 => "seal/v0/commit/1",
            &Self::Commit2 => "seal/v0/commit/2",
            &Self::Finalize => "seal/v0/finalize",
            &Self::Fetch => "seal/v0/fetch",
            &Self::Unseal => "seal/v0/unseal",
            &Self::ReplicaUpdate => "seal/v0/replicaupdate",
            &Self::ProveReplicaUpdate1 => "eal/v0/provereplicaupdate/1",
            &Self::ProveReplicaUpdate2 => "seal/v0/provereplicaupdate/2",
            &Self::RegenSectorKey => "seal/v0/regensectorkey",
            &Self::FinalizeReplicaUpdate => "seal/v0/finalize/replicaupdate",
        }
    }
}

impl Serialize for TaskType {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where
            S: Serializer,
    {
        serializer.serialize_str(self.name())
    }
}

impl<'de> Deserialize<'de> for TaskType {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where
            D: Deserializer<'de>,
    {
        deserializer.deserialize_str(TaskTypeVisitor::new())
    }
}

struct TaskTypeVisitor;

impl TaskTypeVisitor {
    fn new() -> TaskTypeVisitor {
        TaskTypeVisitor {}
    }
}

impl<'de> Visitor<'de> for TaskTypeVisitor {
    type Value = TaskType;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("a string")
    }

    fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
        where
            E: de::Error,
    {
        let ret = Self::Value::from_str(value);
        match ret {
            Ok(t) => Ok(t),
            Err(e) => Err(E::custom(e)),
        }
    }
}
